<?php
/**
 * Une classe de structure qui implemente les observateur (SplObserver)
 *
 * @author jonathan <jo.link.noir@gmail.com>
 * @version 1.1: jo_link_noir
 * @date 06-07-2010
 */
abstract class SW_Structure_Observer extends SW_Structure implements SW_I_Element_Observer
{
	/** @var string */
	const CLASS_ELEMENT = 'SW_Structure_Subject_Element';


	/** @var array : {id_element => SW_Structure_Observer, ...} les attach à mettre sur les élements lors de leurs création*/
	protected $attachElements = array();


	/**
	 * Est lancer quand un obervateur et déclencher et la structure initialisé
	 * @param SW_I_Element_Subject $subject
	 */
	abstract protected function _update(SW_I_Element_Subject $subject);


	/**
	 * @param SW_Player $player
	 * @param int $idPlanet
	 * @param SW_Dependency $dependency
	 * @param SW_I_DAO_Structure $dao
	 * @param string $classElement = self::CLASS_ELEMENT : le type de l'élément est vérifier uniquement dans le passage à la méthode add()
	 * @param string $classException = self::CLASS_EXCEPTION
	 */
	public function __construct(SW_Player $player, $idPlanet, SW_Dependency $dependency, SW_Structure_DAO_Interface $dao, $classElement = self::CLASS_ELEMENT, $classException = self::CLASS_EXCEPTION)
	{
		parent::__construct($player, $idPlanet, $dependency, $dao, $classElement, $classException);

		//si élément n'est pas un self::CLASS_ELEMENT
		if (!($classElement === self::CLASS_ELEMENT || in_array(self::CLASS_ELEMENT, class_parents($classElement, true), true)))
			throw new $this->classException($this->errorMessages[1], 1);
	}

	/**
	 * Est lancer quand un obervateur et déclencher
	 * @param SW_I_Element_Subject $subject
	 */
	final public function update(SW_I_Element_Subject $subject)
	{
		if ($this->isInit())
			$this->_update($subject);
	}

	/**
	 * Ajoute un élement. Si $element n'est pas du type de SW_Structure_Subject_Element une exception est lancée
	 * @param SW_Element $element
	 * @exception SW_Structure_Exception
	 */
	public function add(SW_Element $element)
	{
		parent::add($element);

		//si des observateurs on été attaché, les attachés et les lancés
		if (isset($this->attachElements[$element->getGroup()]))
		{
			foreach ($this->attachElements[$element->getGroup()] as $o)
			{
				$element->attach($o);
				$o->update($element);
			}
		}
	}

	/**
	 * Attache $observer sur un élements et ceux à venir
	 * @param int $group
	 * @param SW_I_Element_Observer $observer
	 * @return SW_Structure_Observer
	 * TODO il n'y a pas de vérification sur l'existance d'un obervateur (peut donc être en doublon)
	 */
	public function attachOnGroup($group, SW_I_Element_Observer $observer)
	{
		//sauvegarde pour attaché aux éléments à venir
		$this->attachElements[$group][] = $observer;

		//attache aux éléments déjà présent
		if (isset($this->groupElements[$group]))
			foreach ($this->groupElements[$group] as $element)
				$element->attach($observer);

		return $this;
	}

	/**
	 * Détache $observer d'un groupe d'élément
	 * @param int $group
	 * @param SplObserver $observer
	 */
	public function detachElement($group, SW_I_Element_Observer $observer)
	{
		if (isset($this->attachElements[$group]))
		{
			foreach ($this->attachElements[$group] as $index => $o)
			{
				if ($o === $observer)
				{
					unset($this->attachElements[$group][$index]);
				}
			}
		}
		foreach ($this->groupElements[$group] as $o)
		{
			$o->detach();
		}
	}

	/**
	 * Detache $observer de tous les éléments
	 * @param SplObserver $observer
	 */
	public function detachAllElement(SW_I_Element_Observer $observer)
	{
		foreach ($this->attachElements as $element_observer)
		{
			foreach ($element_observer as $index => $o)
			{
				if ($o === $observer)
				{
					unset($element_observer[$index]);
				}
			}
		}
		foreach ($this->structure as $o)
		{
			$o->detach();
		}
	}

	/*
	 * Fini la construction de la structure demander
	 * @param SW_Structure_List $list
	 * @param int $timestamp
	 * @exception SW_Structure_Exception
	 */
	/*protected function _finish(SW_Structure_List $list, $timestamp)
	{
		parent::_finish($list, $timestamp);
// 		$this->update($this->elements[$list->getIdElement()]);
// 		$this->elements[$list->getIdElement()]->notify();
	}*/
}